"""
JSProcPool. A process pool that executes JavaScript code in a remote process.

To use this, enable it and define a way in game to call remote on the pool.
You can also define new amp.Commands implemented by JSProcPoolParent to give
JavaScript Evennia manipulating functionality. Be sure to add a stub for them
in the JavaScript global object defined in JSProcPoolChild.
"""

import json
import PyV8

import ev
from twisted.protocols import amp
from src.utils.ampoule.child import AMPChild

# handle global setups
_LOGGER = None

# Script API messages. These are commands used by running scripts
# to interact with the Evennia server process.
# Currently we only implement one of these.

class SendMessage(amp.Command):
    """
    Sends a message to a user.

    jsonargs - a JSON object with a field 'id', identifying the user and
               a field 'msg', containing the string to show them.
    errors - All encompassing error handler
    """
    arguments = [('jsonargs', amp.String())]
    errors = [(Exception, 'EXCEPTION')]
    requiresAnswer = False

class JSProcPoolParent(amp.AMP):
    def msg(self, jsonargs):
        """Handles the SendMessage script API call."""
        # A real implementation would care about object not found, would
        # probably permit searching by name, and the like.
        # For this PoC, this is fine, however.
        decoded = json.loads(jsonargs)
        dbref = decoded['id']
        msg = decoded['msg']
        obj = ev.managers.objects.get_id(dbref)
        obj.msg(msg)
    SendMessage.responder(msg)

# Evennia command to offload JavaScript execution

class ExecuteJavaScript(amp.Command):
    """
    Executes JavaScript.

    source - a compileable JavaScript code string
    environment - A JSON dictionary containing the following:
                'me' : The id of the calling object.
                'here' : The caller's location.
    errors - an all-encompassing error handler

    """
    arguments = [('source', amp.String()),
                 ('environment', amp.String())]
    errors = [(Exception, 'EXCEPTION')]
    requiresAnsweer = False


# Evennia multiprocess child process template
class JSProcPoolChild(AMPChild):
    """
    This is describing what happens on the subprocess side.

    This already supports Echo, Shutdown and Ping.

    Methods:
    execute_javascript - a remote code execution environment
    """
    def execute_javascript(self, source, environment):
        """
        Remote code execution

        source - JavaScript code string
        environment - JSON dictionary of environment. Stores
                        'me' and 'here' only, as id's. These
                        will be made available on the global
                        object.
        """
        # Decode the environment.
        env = json.loads(environment)

        callRemote = self.callRemote
        
        # Create a global object containing me, here, and a function
        # to send a message.
        class Global(PyV8.JSClass):
            me = env['me']
            here = env['here']
            def msg(self, target, msg):
                """
                This function, defined in Python, can be called in JavaScript.
                A real implementation would check argument types and the like.
                """
                args = {'id' : target, 'msg' : msg }
                callRemote(SendMessage,jsonargs=json.dumps(args))
        # Now create a JavaScript context and hand it the code.
        with PyV8.JSContext(Global()) as ctxt:
            ctxt.eval(source)

        return {}
    ExecuteJavaScript.responder(execute_javascript)
